/**
 * Created with CLion.
 * Description: 仅用于测试语音合成与识别功能
 * User: loong
 * Date: 2019-12-08
 * Time: 21:14
 */
#include "src/view/MyVoice.h"
#include "src/view/MyPcm.h"
#include <stdio.h>
#include <time.h>
#include <msp_errors.h>
#include <msp_cmn.h>
#include <qtts.h>
#include <qisr.h>


/**
 * 合成的wav声音文件：默认wav音频头部数据
 */
wave_pcm_hdr default_wav_hdr =
        {
                {'R', 'I', 'F', 'F'},
                0,
                {'W', 'A', 'V', 'E'},
                {'f', 'm', 't', ' '},
                16,
                1,
                1,
                16000,
                32000,
                2,
                16,
                {'d', 'a', 't', 'a'},
                0
        };

Voice voice;
//保存时间
char my_time[25] = {0};
//保存文件名
char my_fileName[50] = {0};

/*
 * 统一输出
 */
void show_sys_info(char *str) {
    printf("%s", str);
}


/**
 * 打开控制灯
 */
void open_voice_light() {
    digitalWrite(VOICE_LIGHT_PIN, HIGH);
}

/**
 * 关闭控制灯
 */
void close_voice_light() {
    digitalWrite(VOICE_LIGHT_PIN, LOW);
}

/**
 * 获取当前时间，在发送信息到服务器中时调用
 * @return
 */
char *getCurrentTimeStr() {
    time_t t = time(NULL);
    strftime(my_time, sizeof(my_time) - 1, "%Y-%m-%d~%H-%M-%S", localtime(&t));   //年-月-日 时-分-秒
    return my_time;
}

/**
 * 功能：利用当前时间，对生成的语音文件进行命名
 */
char *getCaptureAudioFileName() {
    strcat(my_fileName, "../audio/");
    strcat(my_fileName, "Capture");
    strcat(my_fileName, getCurrentTimeStr());
    strcat(my_fileName, ".wav");

    return my_fileName;
}
char *getPlayAudioFileName() {
    strcat(my_fileName, "../audio/");
    strcat(my_fileName, "Play");
    strcat(my_fileName, getCurrentTimeStr());
    strcat(my_fileName, ".wav");

    return my_fileName;
}


/**
 * 功能：讯飞初始化
 * @param xfyun_appid 讯飞appid
 * @return 0为已经登录，-1为错误。
 */
int xfyun_init(const char *xfyun_appid) {

    memset(voice.xfyun_appid, 0, sizeof(voice.xfyun_appid));
    //科大讯飞设置appid
    if (xfyun_appid == NULL || strlen(xfyun_appid) <= 0) {
        printf("voice初始化错误：讯飞APPID为空，\n");
    } else {
        strcpy(voice.xfyun_appid, xfyun_appid);
    }

    //如果当前已经开启语音功能，即登陆过科大讯飞
    if (voice.voice_main_switch == SWITCH_ON) {
        return 0;
    }
    int ret = MSP_SUCCESS;

    //设置讯飞登录参数，并填充
    char login_params[100];
    memset(login_params, 0, sizeof(login_params));
    strcat(login_params, "appid = ");
    strcat(login_params, voice.xfyun_appid);
    strcat(login_params, ", work_dir = .");

    /* 用户登录 */
    //第一个参数是用户名，第二个参数是密码，均传NULL即可，第三个参数是登录参数
    ret = MSPLogin(NULL, NULL, login_params);
    if (MSP_SUCCESS != ret) {
        printf("MSPLogin failed (科大讯飞账号登陆失败！) , Error code %d.\n", ret);
        printf("voice初始化：语音识别初始化失败！\n");

        //退出登录
        MSPLogout();
        return -1;
    }

    //已登陆过科大讯飞
    voice.voice_main_switch = SWITCH_ON;
    printf("voice设置启动模式：网络连接模式，语音识别系统启动.\n ");
    return 0;
}

/**
 * 功能：设备参数初始化
 * @param audio_addr 设备地址
 */
void pcm_device_init(const char *audio_addr) {

    //声音总开关（如果语音初始化失败，则为关闭状态！）
    voice.voice_main_switch = SWITCH_OFF;
    //标识当前是否打开语音识别 （高）
    voice.recongnition_switch = SWITCH_OFF;
    //标识当前音箱状态，等于0为没有说话，小于0为正在说话
    voice.sound_box_ongoing_flag = 0;

    //默认属性初始化声音参数内存空间的线程锁
    pthread_mutex_init(&(voice.mutex_voice_params), NULL);

    //音频转换成文字后保存到这里
    memset(voice.voice_recongnition_text, 0, sizeof(voice.voice_recongnition_text));
    //设备地址
    memset(voice.usb_audio_addr, 0, sizeof(voice.usb_audio_addr));

    //USB声卡设置
    if (audio_addr == NULL || strlen(audio_addr) <= 0) {
        printf("voice初始化错误：USB声卡地址信息为空，语音聊天控制功能将有限制！\n");
    } else {
        strcpy(voice.usb_audio_addr, audio_addr);
    }

    printf("voice总初始化：成功！\n");
}

/**
 * 功能：设置声音播放参数初始化
 * @param voice_name    发音人，不同的发音人代表了不同的音色，如男声、女声、童声等，
 * @param speed         语速，合成音频对应的语速，取值范围：[0,100]，数值越大语速越快。
 * @param volume        音量，合成音频的音量，取值范围：[0,100]，数值越大音量越大。
 * @param pitch         语调，	合成音频的音调，取值范围：[0,100]，数值越大音调越高。
 * @param bgm           背景音，合成音频中的背景音，支持参数：0：无背景音乐，1：有背景音乐
 * @return              成功返回0，失败返回-1。
 */
int set_voice_params(char *voice_name, int speed, int volume, int pitch, int bgm) {

    //音频输出的参数包括：发音人，语速，音量，语调
    memset(voice.output_voice_params, 0, sizeof(voice.output_voice_params));

    if (strlen(voice_name) > 15 || speed > 100 || speed < 0 || volume > 100 || volume < 0 || pitch > 100 || pitch < 0 || bgm < 0 || bgm > 1)
        return -1; //参数出错
    char s_speed[10] = {0};
    sprintf(s_speed, "%d", speed);
    char s_volume[10] = {0};
    sprintf(s_volume, "%d", volume);
    char s_pitch[10] = {0};
    sprintf(s_pitch, "%d", pitch);


    pthread_mutex_lock(&(voice.mutex_voice_params)); //申请锁

    memset(voice.output_voice_params, 0, sizeof(voice.output_voice_params));
    strcat(voice.output_voice_params, "voice_name = ");
    //注意：如果当前名字不符合科大讯飞提供的名字，系统会出错，这里不进行正确检查
    strcat(voice.output_voice_params, voice_name);
    strcat(voice.output_voice_params, ", text_encoding = utf8, sample_rate = 16000, ");
    strcat(voice.output_voice_params, "speed = ");
    strcat(voice.output_voice_params, s_speed);
    strcat(voice.output_voice_params, ", volume = ");
    strcat(voice.output_voice_params, s_volume);
    strcat(voice.output_voice_params, ", pitch = ");
    strcat(voice.output_voice_params, s_pitch);
    if (bgm == 0)
        strcat(voice.output_voice_params, ", rdn = 2, background_sound = 0");
    else
        strcat(voice.output_voice_params, ", rdn = 2, background_sound = 1");

    pthread_mutex_unlock(&(voice.mutex_voice_params)); //释放锁

    return 0;
}

/**
 * 功能：讯飞云进行语音合成
 * @param handle        pcm设备句柄
 * @param src_text      需要转成的文字
 * @param size          数据块长度
 * @return              错误返回讯飞错误码
 */
int xfyun_tts(char *src_text, const char *des_path, const char *voice_putput_params) {
    int ret = -1;

    const char *sessionID = NULL;
    unsigned int audio_len = 0;
    wave_pcm_hdr wav_hdr = default_wav_hdr;
    int synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;

    if (NULL == src_text || NULL == des_path) {
        printf("params is error!\n");
        return ret;
    }
    FILE *fp = NULL;
    fp = fopen(des_path, "wb");
    if (NULL == fp) {
        printf("open %s error.\n", des_path);
        return ret;
    }
    /* 开始合成 */
    pthread_mutex_lock(&(voice.mutex_voice_params)); //申请锁
    sessionID = QTTSSessionBegin(voice_putput_params, &ret);
    pthread_mutex_unlock(&(voice.mutex_voice_params)); //释放锁
    if (MSP_SUCCESS != ret) {
        printf("QTTSSessionBegin failed, error code: %d.\n", ret);
        fclose(fp);
        return ret;
    }
    ret = QTTSTextPut(sessionID, src_text, (unsigned int) strlen(src_text), NULL);
    if (MSP_SUCCESS != ret) {
        printf("QTTSTextPut failed, error code: %d.\n", ret);
        QTTSSessionEnd(sessionID, "TextPutError");
        fclose(fp);
        return ret;
    }
    printf("正在合成 ...\n");
    //添加wav音频头，使用采样率为16000
    fwrite(&wav_hdr, sizeof(wav_hdr), 1, fp);
    while (1) {
        /* 获取合成音频 */
        const void *data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
        if (MSP_SUCCESS != ret)
            break;
        if (NULL != data) {
            fwrite(data, audio_len, 1, fp);
            wav_hdr.data_size += audio_len; //计算data_size大小
        }
        if (MSP_TTS_FLAG_DATA_END == synth_status)
            break;
        printf(">");
        usleep(150 * 1000); //防止频繁占用CPU
    }
    printf("\n");
    if (MSP_SUCCESS != ret) {
        printf("QTTSAudioGet failed, error code: %d.\n", ret);
        QTTSSessionEnd(sessionID, "AudioGetError");
        fclose(fp);
        return ret;
    }
    /* 修正wav文件头数据的大小 */
    wav_hdr.size_8 += wav_hdr.data_size + (sizeof(wav_hdr) - 8);
    /* 将修正过的数据写回文件头部,音频文件为wav格式 */
    fseek(fp, 4, 0);
    //写入size_8的值
    fwrite(&wav_hdr.size_8, sizeof(wav_hdr.size_8), 1, fp);
    //将文件指针偏移到存储data_size值的位置
    fseek(fp, 40, 0);
    //写入data_size的值
    fwrite(&wav_hdr.data_size, sizeof(wav_hdr.data_size), 1, fp);
    //关闭文件
    fclose(fp);

    /* 合成完毕 */
    ret = QTTSSessionEnd(sessionID, "Normal");
    if (MSP_SUCCESS != ret) {
        printf("QTTSSessionEnd failed, error code: %d.\n", ret);
    }
    return ret;
}

int xfyun_speech_recognition(const char *audio_file){
    const char *session_begin_params ="sub = iat, domain = iat, language = zh_cn, accent = mandarin, sample_rate = 16000, result_type = plain, result_encoding = utf8";
    const char *session_id = NULL;
    char rec_result[BUFFER_SIZE] = {NULL};
    char hints[HINTS_SIZE] = {NULL}; //hints为结束本次会话的原因描述，由用户自定义
    unsigned int total_len = 0;
    //音频状态
    int aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;
    //端点检测
    int ep_stat = MSP_EP_LOOKING_FOR_SPEECH;
    //识别状态
    int rec_stat = MSP_REC_STATUS_SUCCESS;

    int errcode = MSP_SUCCESS;

    FILE *f_pcm = NULL;
    char *p_pcm = NULL;
    long pcm_count = 0;
    long pcm_size = 0;
    long read_size = 0;


    if (NULL == audio_file)
        goto iat_exit;

    f_pcm = fopen(audio_file, "rb");
    if (NULL == f_pcm) {
        printf("\nopen [%s] failed! \n", audio_file);
        goto iat_exit;
    }

    fseek(f_pcm, 0, SEEK_END);
    pcm_size = ftell(f_pcm); //获取音频文件大小
    fseek(f_pcm, 0, SEEK_SET);

    p_pcm = (char *) malloc(pcm_size);
    if (NULL == p_pcm) {
        printf("\nout of memory! \n");
        goto iat_exit;
    }

    read_size = fread((void *) p_pcm, 1, pcm_size, f_pcm); //读取音频文件内容
    if (read_size != pcm_size) {
        printf("\nread [%s] error!\n", audio_file);
        goto iat_exit;
    }

    printf("\n开始语音听写 ...\n");
    session_id = QISRSessionBegin(NULL, session_begin_params, &errcode); //听写不需要语法，第一个参数为NULL
    if (MSP_SUCCESS != errcode) {
        printf("\nQISRSessionBegin failed! error code:%d\n", errcode);
        goto iat_exit;
    }

    while (1) {
        // 每次写入200ms音频(16k，16bit)：1帧音频20ms，10帧=200ms。16k采样率的16位音频，一帧的大小为640Byte
        unsigned int len = 10 * FRAME_LEN;
        int ret = 0;

        if (pcm_size < 2 * len)
            len = pcm_size;
        if (len <= 0)
            break;

        aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;
        if (0 == pcm_count)
            aud_stat = MSP_AUDIO_SAMPLE_FIRST;

        printf(">");
        ret = QISRAudioWrite(session_id, (const void *) &p_pcm[pcm_count], len, aud_stat, &ep_stat, &rec_stat);
        if (MSP_SUCCESS != ret) {
            printf("\nQISRAudioWrite failed! error code:%d\n", ret);
            goto iat_exit;
        }

        pcm_count += (long) len;
        pcm_size -= (long) len;

        if (MSP_REC_STATUS_SUCCESS == rec_stat) //已经有部分听写结果
        {
            const char *rslt = QISRGetResult(session_id, &rec_stat, 0, &errcode);
            if (MSP_SUCCESS != errcode) {
                printf("\nQISRGetResult failed! error code: %d\n", errcode);
                goto iat_exit;
            }
            if (NULL != rslt) {
                unsigned int rslt_len = strlen(rslt);
                total_len += rslt_len;
                if (total_len >= BUFFER_SIZE) {
                    printf("\nno enough buffer for rec_result !\n");
                    goto iat_exit;
                }
                strncat(rec_result, rslt, rslt_len);
            }
        }

        if (MSP_EP_AFTER_SPEECH == ep_stat)
            break;
        usleep(200 * 1000); //模拟人说话时间间隙。200ms对应10帧的音频
    }
    errcode = QISRAudioWrite(session_id, NULL, 0, MSP_AUDIO_SAMPLE_LAST, &ep_stat, &rec_stat);
    if (MSP_SUCCESS != errcode) {
        printf("\nQISRAudioWrite failed! error code:%d \n", errcode);
        goto iat_exit;
    }

    while (MSP_REC_STATUS_COMPLETE != rec_stat) {
        const char *rslt = QISRGetResult(session_id, &rec_stat, 0, &errcode);
        if (MSP_SUCCESS != errcode) {
            printf("\nQISRGetResult failed, error code: %d\n", errcode);
            goto iat_exit;
        }
        if (NULL != rslt) {
            unsigned int rslt_len = strlen(rslt);
            total_len += rslt_len;
            if (total_len >= BUFFER_SIZE) {
                printf("\nno enough buffer for rec_result !\n");
                goto iat_exit;
            }
            strncat(rec_result, rslt, rslt_len);
        }
        usleep(150 * 1000); //防止频繁占用CPU
    }
    printf("\n语音听写结束\n");
    printf("%s\n", rec_result);

    iat_exit:
    if (NULL != f_pcm) {
        fclose(f_pcm);
        f_pcm = NULL;
    }
    if (NULL != p_pcm) {
        free(p_pcm);
        p_pcm = NULL;
    }

    QISRSessionEnd(session_id, hints);
}
/**
 * 功能：文字转声音并播放（在线播放）
 * @param src_text 转换文字
 * @return 错误返回-1，正确返回0.
 */
int text_to_speech(char *src_text) {
    int ret = 0;
    if (NULL == src_text) {
        printf("参数错误，输入文件为空！\n");
        return -1;
    }

    //snd_pcm_t *handle; //PCI设备句柄
    //合成的语音文件地址及名称
    const char *filename = getPlayAudioFileName();

    //使用讯飞接口进行TTS服务。并将
    ret = xfyun_tts(src_text, filename, voice.output_voice_params);
    if (ret <0){
        perror("讯飞语音合成错误\n");
        return -1;
    }
    ret = playBack(default_wav_hdr, filename);
    if (ret < 0){
        perror("设备播放错误\n");
        return -1;
    }
    return 0;
}

/**
 * 功能：语音识别
 * @return 错误返回-1，正确返回0.
 */
int speech_recognition(){
    int ret = 0;
    const char *filename = getCaptureAudioFileName();

    ret = capture(default_wav_hdr,filename);
    if (ret<0){
        perror("文件捕捉错误\n");
        return -1;
    }
    ret = xfyun_speech_recognition(filename);
    if (ret < 0){
        perror("语音识别错误\n");
        return -1;
    }
    return 0;
}